home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / Python 1.1 / Demo / rpc / rpc.py < prev    next >
Encoding:
Text File  |  1993-12-17  |  21.6 KB  |  677 lines  |  [TEXT/R*ch]

  1. TED = 0
  2. MSG_DENIED = 1
  3.  
  4. SUCCESS = 0                # RPC executed successfully
  5. PROG_UNAVAIL  = 1            # remote hasn't exported program
  6. PROG_MISMATCH = 2            # remote can't support version #
  7. PROC_UNAVAIL  = 3            # program can't support procedure
  8. GARBAGE_ARGS  = 4            # procedure can't decode params
  9.  
  10. RPC_MISMATCH = 0            # RPC version number != 2
  11. AUTH_ERROR = 1                # remote can't authenticate caller
  12.  
  13. AUTH_BADCRED      = 1            # bad credentials (seal broken)
  14. AUTH_REJECTEDCRED = 2            # client must begin new session
  15. AUTH_BADVERF      = 3            # bad verifier (seal broken)
  16. AUTH_REJECTEDVERF = 4            # verifier expired or replayed
  17. AUTH_TOOWEAK      = 5            # rejected for security reasons
  18.  
  19.  
  20. class Packer(xdr.Packer):
  21.  
  22.     def pack_auth(self, auth):
  23.         flavor, stuff = auth
  24.         self.pack_enum(flavor)
  25.         self.pack_opaque(stuff)
  26.  
  27.     def pack_auth_unix(self, stamp, machinename, uid, gid, gids):
  28.         self.pack_uint(stamp)
  29.         self.pack_string(machinename)
  30.         self.pack_uint(uid)
  31.         self.pack_uint(gid)
  32.         self.pack_uint(len(gids))
  33.         for i in gids:
  34.             self.pack_uint(i)
  35.  
  36.     def pack_callheader(self, xid, prog, vers, proc, cred, verf):
  37.         self.pack_uint(xid)
  38.         self.pack_enum(CALL)
  39.         self.pack_uint(RPCVERSION)
  40.         self.pack_uint(prog)
  41.         self.pack_uint(vers)
  42.         self.pack_uint(proc)
  43.         self.pack_auth(cred)
  44.         self.pack_auth(verf)
  45.         # Caller must add procedure-specific part of call
  46.  
  47.     def pack_replyheader(self, xid, verf):
  48.         self.pack_uint(xid)
  49.         self.pack_enum(REPLY)
  50.         self.pack_uint(MSG_ACCEPTED)
  51.         self.pack_auth(verf)
  52.         self.pack_enum(SUCCESS)
  53.         # Caller must add procedure-specific part of reply
  54.  
  55.  
  56. # Exceptions
  57. BadRPCFormat = 'rpc.BadRPCFormat'
  58. BadRPCVersion = 'rpc.BadRPCVersion'
  59. GarbageArgs = 'rpc.GarbageArgs'
  60.  
  61. class Unpacker(xdr.Unpacker):
  62.  
  63.     def unpack_auth(self):
  64.         flavor = self.unpack_enum()
  65.         stuff = self.unpack_opaque()
  66.         return (flavor, stuff)
  67.  
  68.     def unpack_callheader(self):
  69.         xid = self.unpack_uint(xid)
  70.         temp = self.unpack_enum()
  71.         if temp <> CALL:
  72.             raise BadRPCFormat, 'no CALL but ' + `temp`
  73.         temp = self.unpack_uint()
  74.         if temp <> RPCVERSION:
  75.             raise BadRPCVerspion, 'bad RPC version ' + `temp`
  76.         prog = self.unpack_uint()
  77.         vers = self.unpack_uint()
  78.         proc = self.unpack_uint()
  79.         cred = self.unpack_auth()
  80.         verf = self.unpack_auth()
  81.         return xid, prog, vers, proc, cred, verf
  82.         # Caller must add procedure-specific part of call
  83.  
  84.     def unpack_replyheader(self):
  85.         xid = self.unpack_uint()
  86.         mtype = self.unpack_enum()
  87.         if mtype <> REPLY:
  88.             raise RuntimeError, 'no REPLY but ' + `mtype`
  89.         stat = self.unpack_enum()
  90.         if stat == MSG_DENIED:
  91.             stat = self.unpack_enum()
  92.             if stat == RPC_MISMATCH:
  93.                 low = self.unpack_uint()
  94.                 high = self.unpack_uint()
  95.                 raise RuntimeError, \
  96.                   'MSG_DENIED: RPC_MISMATCH: ' + `low, high`
  97.             if stat == AUTH_ERROR:
  98.                 stat = self.unpack_uint()
  99.                 raise RuntimeError, \
  100.                     'MSG_DENIED: AUTH_ERROR: ' + `stat`
  101.             raise RuntimeError, 'MSG_DENIED: ' + `stat`
  102.         if stat <> MSG_ACCEPTED:
  103.             raise RuntimeError, \
  104.               'Neither MSG_DENIED nor MSG_ACCEPTED: ' + `stat`
  105.         verf = self.unpack_auth()
  106.         stat = self.unpack_enum()
  107.         if stat == PROG_UNAVAIL:
  108.             raise RuntimeError, 'call failed: PROG_UNAVAIL'
  109.         if stat == PROG_MISMATCH:
  110.             low = self.unpack_uint()
  111.             high = self.unpack_uint()
  112.             raise RuntimeError, \
  113.                 'call failed: PROG_MISMATCH: ' + `low, high`
  114.         if stat == PROC_UNAVAIL:
  115.             raise RuntimeError, 'call failed: PROC_UNAVAIL'
  116.         if stat == GARBAGE_ARGS:
  117.             raise RuntimeError, 'call failed: GARBAGE_ARGS'
  118.         if stat <> SUCCESS:
  119.             raise RuntimeError, 'call failed: ' + `stat`
  120.         return xid, verf
  121.         # Caller must get procedure-specific part of reply
  122.  
  123.  
  124. # Subroutines to create opaque authentication objects
  125.  
  126. def make_auth_null():
  127.     return ''
  128.  
  129. def make_auth_unix(seed, host, uid, gid, groups):
  130.     p = Packer()
  131.     p.pack_auth_unix(seed, host, uid, gid, groups)
  132.     return p.get_buf()
  133.  
  134. def make_auth_unix_default():
  135.     try:
  136.         from os import getuid, getgid
  137.         uid = getuid()
  138.         gid = getgid()
  139.     except ImportError:
  140.         uid = gid = 0
  141.     import time
  142.     return make_auth_unix(int(time.time()), \
  143.           socket.gethostname(), uid, gid, [])
  144.  
  145.  
  146. # Common base class for clients
  147.  
  148. class Client:
  149.  
  150.     def __init__(self, host, prog, vers, port):
  151.         self.host = host
  152.         self.prog = prog
  153.         self.vers = vers
  154.         self.port = port
  155.         self.makesocket() # Assigns to self.sock
  156.         self.bindsocket()
  157.         self.connsocket()
  158.         self.lastxid = 0 # XXX should be more random?
  159.         self.addpackers()
  160.         self.cred = None
  161.         self.verf = None
  162.  
  163.     def close(self):
  164.         self.sock.close()
  165.  
  166.     def makesocket(self):
  167.         # This MUST be overridden
  168.         raise RuntimeError, 'makesocket not defined'
  169.  
  170.     def connsocket(self):
  171.         # Override this if you don't want/need a connection
  172.         self.sock.connect((self.host, self.port))
  173.  
  174.     def bindsocket(self):
  175.         # Override this to bind to a different port (e.g. reserved)
  176.         self.sock.bind(('', 0))
  177.  
  178.     def addpackers(self):
  179.         # Override this to use derived classes from Packer/Unpacker
  180.         self.packer = Packer()
  181.         self.unpacker = Unpacker('')
  182.  
  183.     def make_call(self, proc, args, pack_func, unpack_func):
  184.         # Don't normally override this (but see Broadcast)
  185.         if pack_func is None and args is not None:
  186.             raise TypeError, 'non-null args with null pack_func'
  187.         self.start_call(proc)
  188.         if pack_func:
  189.             pack_func(args)
  190.         self.do_call()
  191.         if unpack_func:
  192.             result = unpack_func()
  193.         else:
  194.             result = None
  195.         self.unpacker.done()
  196.         return result
  197.  
  198.     def start_call(self, proc):
  199.         # Don't override this
  200.         self.lastxid = xid = self.lastxid + 1
  201.         cred = self.mkcred()
  202.         verf = self.mkverf()
  203.         p = self.packer
  204.         p.reset()
  205.         p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
  206.  
  207.     def do_call(self):
  208.         # This MUST be overridden
  209.         raise RuntimeError, 'do_call not defined'
  210.  
  211.     def mkcred(self):
  212.         # Override this to use more powerful credentials
  213.         if self.cred == None:
  214.             self.cred = (AUTH_NULL, make_auth_null())
  215.         return self.cred
  216.  
  217.     def mkverf(self):
  218.         # Override this to use a more powerful verifier
  219.         if self.verf == None:
  220.             self.verf = (AUTH_NULL, make_auth_null())
  221.         return self.verf
  222.  
  223.     def call_0(self):        # Procedure 0 is always like this
  224.         return self.make_call(0, None, None, None)
  225.  
  226.  
  227. # Record-Marking standard support
  228.  
  229. def sendfrag(sock, last, frag):
  230.     x = len(frag)
  231.     if last: x = x | 0x80000000L
  232.     header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
  233.           chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
  234.     sock.send(header + frag)
  235.  
  236. def sendrecord(sock, record):
  237.     sendfrag(sock, 1, record)
  238.  
  239. def recvfrag(sock):
  240.     header = sock.recv(4)
  241.     if len(header) < 4:
  242.         raise EOFError
  243.     x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \
  244.         ord(header[2])<<8 | ord(header[3])
  245.     last = ((x & 0x80000000) != 0)
  246.     n = int(x & 0x7fffffff)
  247.     frag = ''
  248.     while n > 0:
  249.         buf = sock.recv(n)
  250.         if not buf: raise EOFError
  251.         n = n - len(buf)
  252.         frag = frag + buf
  253.     return last, frag
  254.  
  255. def recvrecord(sock):
  256.     record = ''
  257.     last = 0
  258.     while not last:
  259.         last, frag = recvfrag(sock)
  260.         record = record + frag
  261.     return record
  262.  
  263.  
  264. # Try to bind to a reserved port (must be root)
  265.  
  266. last_resv_port_tried = None
  267. def bindresvport(sock, host):
  268.     global last_resv_port_tried
  269.     FIRST, LAST = 600, 1024 # Range of ports to try
  270.     if last_resv_port_tried == None:
  271.         import os
  272.         last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST)
  273.     for i in range(last_resv_port_tried, LAST) + \
  274.           range(FIRST, last_resv_port_tried):
  275.         last_resv_port_tried = i
  276.         try:
  277.             sock.bind((host, i))
  278.             return last_resv_port_tried
  279.         except socket.error, (errno, msg):
  280.             if errno <> 114:
  281.                 raise socket.error, (errno, msg)
  282.     raise RuntimeError, 'can\'t assign reserved port'
  283.  
  284.  
  285. # Client using TCP to a specific port
  286.  
  287. class RawTCPClient(Client):
  288.  
  289.     def makesocket(self):
  290.         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  291.  
  292.     def do_call(self):
  293.         call = self.packer.get_buf()
  294.         sendrecord(self.sock, call)
  295.         reply = recvrecord(self.sock)
  296.         u = self.unpacker
  297.         u.reset(reply)
  298.         xid, verf = u.unpack_replyheader()
  299.         if xid <> self.lastxid:
  300.             # Can't really happen since this is TCP...
  301.             raise RuntimeError, 'wrong xid in reply ' + `xid` + \
  302.                 ' instead of ' + `self.lastxid`
  303.  
  304.  
  305. # Client using UDP to a specific port
  306.  
  307. class RawUDPClient(Client):
  308.  
  309.     def makesocket(self):
  310.         self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  311.  
  312.     def do_call(self):
  313.         call = self.packer.get_buf()
  314.         self.soc
  315.             self.packer.pack_mapping, \
  316.             self.unpacker.unpack_uint)
  317.  
  318.     def Unset(self, mapping):
  319.         return self.make_call(PMAPPROC_UNSET, mapping, \
  320.             self.packer.pack_mapping, \
  321.             self.unpacker.unpack_uint)
  322.  
  323.     def Getport(self, mapping):
  324.         return self.make_call(PMAPPROC_GETPORT, mapping, \
  325.             self.packer.pack_mapping, \
  326.             self.unpacker.unpack_uint)
  327.  
  328.     def Dump(self):
  329.         return self.make_call(PMAPPROC_DUMP, None, \
  330.             None, \
  331.             self.unpacker.unpack_pmaplist)
  332.  
  333.     def Callit(self, ca):
  334.         return self.make_call(PMAPPROC_CALLIT, ca, \
  335.             self.packer.pack_call_args, \
  336.             self.unpacker.unpack_call_result)
  337.  
  338.  
  339. class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient):
  340.  
  341.     def __init__(self, host):
  342.         RawTCPClient.__init__(self, \
  343.             host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
  344.  
  345.  
  346. class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient):
  347.  
  348.     def __init__(self, host):
  349.         RawUDPClient.__init__(self, \
  350.             host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
  351.  
  352.  
  353. class BroadcastUDPPortMapperClient(PartialPortMapperClient, \
  354.                    RawBroadcastUDPClient):
  355.  
  356.     def __init__(self, bcastaddr):
  357.         RawBroadcastUDPClient.__init__(self, \
  358.             bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT)
  359.  
  360.  
  361. # Generic clients that find their server through the Port mapper
  362.  
  363. class TCPClient(RawTCPClient):
  364.  
  365.     def __init__(self, host, prog, vers):
  366.         pmap = TCPPortMapperClient(host)
  367.         port = pmap.Getport((prog, vers, IPPROTO_TCP, 0))
  368.         pmap.close()
  369.         if port == 0:
  370.             raise RuntimeError, 'program not registered'
  371.         RawTCPClient.__init__(self, host, prog, vers, port)
  372.  
  373.  
  374. class UDPClient(RawUDPClient):
  375.  
  376.     def __init__(self, host, prog, vers):
  377.         pmap = UDPPortMapperClient(host)
  378.         port = pmap.Getport((prog, vers, IPPROTO_UDP, 0))
  379.         pmap.close()
  380.         if port == 0:
  381.             raise RuntimeError, 'program not registered'
  382.         RawUDPClient.__init__(self, host, prog, vers, port)
  383.  
  384.  
  385. class BroadcastUDPClient(Client):
  386.  
  387.     def __init__(self, bcastaddr, prog, vers):
  388.         self.pmap = BroadcastUDPPortMapperClient(bcastaddr)
  389.         self.pmap.set_reply_handler(self.my_reply_handler)
  390.         self.prog = prog
  391.         self.vers = vers
  392.         self.user_reply_handler = None
  393.         self.addpackers()
  394.  
  395.     def close(self):
  396.         self.pmap.close()
  397.  
  398.     def set_reply_handler(self, reply_handler):
  399.         self.user_reply_handler = reply_handler
  400.  
  401.     def set_timeout(self, timeout):
  402.         self.pmap.set_timeout(timeout)
  403.  
  404.     def my_reply_handler(self, reply, fromaddr):
  405.         port, res = reply
  406.         self.unpacker.reset(res)
  407.         result = self.unpack_func()
  408.         self.unpacker.done()
  409.         self.replies.append((result, fromaddr))
  410.         if self.user_reply_handler is not None:
  411.             self.user_reply_handler(result, fromaddr)
  412.  
  413.     def make_call(self, proc, args, pack_func, unpack_func):
  414.         self.packer.reset()
  415.         if pack_func:
  416.             pack_func(args)
  417.         if unpack_func is None:
  418.             def dummy(): pass
  419.             self.unpack_func = dummy
  420.         else:
  421.             self.unpack_func = unpack_func
  422.         self.replies = []
  423.         packed_args = self.packer.get_buf()
  424.         dummy_replies = self.pmap.Callit( \
  425.             (self.prog, self.vers, proc, packed_args))
  426.         return self.replies
  427.  
  428.  
  429. # Server classes
  430.  
  431. # These are not symmetric to the Client classes
  432. # XXX No attempt is made to provide authorization hooks yet
  433.  
  434. class Server:
  435.  
  436.     def __init__(self, host, prog, vers, port):
  437.         self.host = host # Should normally be '' for default interface
  438.         self.prog = prog
  439.         self.vers = vers
  440.         self.port = port # Should normally be 0 for random port
  441.         self.makesocket() # Assigns to self.sock and self.prot
  442.         self.bindsocket()
  443.         self.host, self.port = self.sock.getsockname()
  444.         self.addpackers()
  445.  
  446.     def register(self):
  447.         mapping = self.prog, self.vers, self.prot, self.port
  448.         p = TCPPortMapperClient(self.host)
  449.         if not p.Set(mapping):
  450.             raise RuntimeError, 'register failed'
  451.  
  452.     def unregister(self):
  453.         mapping = self.prog, self.vers, self.prot, self.port
  454.         p = TCPPortMapperClient(self.host)
  455.         if not p.Unset(mapping):
  456.             raise RuntimeError, 'unregister failed'
  457.  
  458.     def handle(self, call):
  459.         # Don't use unpack_header but parse the header piecewise
  460.         # XXX I have no idea if I am using the right error responses!
  461.         self.unpacker.reset(call)
  462.         self.packer.reset()
  463.         xid = self.unpacker.unpack_uint()
  464.         self.packer.pack_uint(xid)
  465.         temp = self.unpacker.unpack_enum()
  466.         if temp <> CALL:
  467.             return None # Not worthy of a reply
  468.         self.packer.pack_uint(REPLY)
  469.         temp = self.unpacker.unpack_uint()
  470.         if temp <> RPCVERSION:
  471.             self.packer.pack_uint(MSG_DENIED)
  472.             self.packer.pack_uint(RPC_MISMATCH)
  473.             self.packer.pack_uint(RPCVERSION)
  474.             self.packer.pack_uint(RPCVERSION)
  475.             return self.packer.get_buf()
  476.         self.packer.pack_uint(MSG_ACCEPTED)
  477.         self.packer.pack_auth((AUTH_NULL, make_auth_null()))
  478.         prog = self.unpacker.unpack_uint()
  479.         if prog <> self.prog:
  480.             self.packer.pack_uint(PROG_UNAVAIL)
  481.             return self.packer.get_buf()
  482.         vers = self.unpacker.unpack_uint()
  483.         if vers <> self.vers:
  484.             self.packer.pack_uint(PROG_MISMATCH)
  485.             self.packer.pack_uint(self.vers)
  486.             self.packer.pack_uint(self.vers)
  487.             return self.packer.get_buf()
  488.         proc = self.unpacker.unpack_uint()
  489.         methname = 'handle_' + `proc`
  490.         try:
  491.             meth = getattr(self, methname)
  492.         except AttributeError:
  493.             self.packer.pack_uint(PROC_UNAVAIL)
  494.             return self.packer.get_buf()
  495.         cred = self.unpacker.unpack_auth()
  496.         verf = self.unpacker.unpack_auth()
  497.         try:
  498.             meth() # Unpack args, call turn_around(), pack reply
  499.         except (EOFError, GarbageArgs):
  500.             # Too few or too many arguments
  501.             self.packer.reset()
  502.             self.packer.pack_uint(xid)
  503.             self.packer.pack_uint(REPLY)
  504.             self.packer.pack_uint(MSG_ACCEPTED)
  505.             self.packer.pack_auth((AUTH_NULL, make_auth_null()))
  506.             self.packer.pack_uint(GARBAGE_ARGS)
  507.         return self.packer.get_buf()
  508.  
  509.     def turn_around(self):
  510.         try:
  511.             self.unpacker.done()
  512.         except RuntimeError:
  513.             raise GarbageArgs
  514.         self.packer.pack_uint(SUCCESS)
  515.  
  516.     def handle_0(self): # Handle NULL message
  517.         self.turn_around()
  518.  
  519.     def makesocket(self):
  520.         # This MUST be overridden
  521.         raise RuntimeError, 'makesocket not defined'
  522.  
  523.     def bindsocket(self):
  524.         # Override this to bind to a different port (e.g. reserved)
  525.         self.sock.bind((self.host, self.port))
  526.  
  527.     def addpackers(self):
  528.         # Override this to use derived classes from Packer/Unpacker
  529.         self.packer = Packer()
  530.         self.unpacker = Unpacker('')
  531.  
  532.  
  533. class TCPServer(Server):
  534.  
  535.     def makesocket(self):
  536.         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  537.         self.prot = IPPROTO_TCP
  538.  
  539.     def loop(self):
  540.         self.sock.listen(0)
  541.         while 1:
  542.             self.session(self.sock.accept())
  543.  
  544.     def session(self, connection):
  545.         sock, (host, port) = connection
  546.         while 1:
  547.             try:
  548.                 call = recvrecord(sock)
  549.             except EOFError:
  550.                 break
  551.             except socket.error, msg:
  552.                 print 'socket error:', msg
  553.                 break
  554.             reply = self.handle(call)
  555.             if reply is not None:
  556.                 sendrecord(sock, reply)
  557.  
  558.     def forkingloop(self):
  559.         # Like loop but uses forksession()
  560.         self.sock.listen(0)
  561.         while 1:
  562.             self.forksession(self.sock.accept())
  563.  
  564.     def forksession(self, connection):
  565.         # Like session but forks off a subprocess
  566.         import os
  567.         # Wait for deceased children
  568.         try:
  569.             while 1:
  570.                 pid, sts = os.waitpid(0, 1)
  571.         except os.error:
  572.             pass
  573.         pid = None
  574.         try:
  575.             pid = os.fork()
  576.             if pid: # Parent
  577.                 connection[0].close()
  578.                 return
  579.             # Child
  580.             self.session(connection)
  581.         finally:
  582.             # Make sure we don't fall through in the parent
  583.             if pid == 0:
  584.                 os._exit(0)
  585.  
  586.  
  587. class UDPServer(Server):
  588.  
  589.     def makesocket(self):
  590.         self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  591.         self.prot = IPPROTO_UDP
  592.  
  593.     def loop(self):
  594.         while 1:
  595.             self.session()
  596.  
  597.     def session(self):
  598.         call, host_port = self.sock.recvfrom(8192)
  599.         reply = self.handle(call)
  600.         if reply <> None:
  601.             self.sock.sendto(reply, host_port)
  602.  
  603.  
  604. # Simple test program -- dump local portmapper status
  605.  
  606. def test():
  607.     pmap = UDPPortMapperClient('')
  608.     list = pmap.Dump()
  609.     list.sort()
  610.     for prog, vers, prot, port in list:
  611.         print prog, vers,
  612.         if prot == IPPROTO_TCP: print 'tcp',
  613.         elif prot == IPPROTO_UDP: print 'udp',
  614.         else: print prot,
  615.         print port
  616.  
  617.  
  618. # Test program for broadcast operation -- dump everybody's portmapper status
  619.  
  620. def testbcast():
  621.     import sys
  622.     if sys.argv[1:]:
  623.         bcastaddr = sys.argv[1]
  624.     else:
  625.         bcastaddr = '<broadcast>'
  626.     def rh(reply, fromaddr):
  627.         host, port = fromaddr
  628.         print host + '\t' + `reply`
  629.     pmap = BroadcastUDPPortMapperClient(bcastaddr)
  630.     pmap.set_reply_handler(rh)
  631.     pmap.set_timeout(5)
  632.     replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0))
  633.  
  634.  
  635. # Test program for server, with corresponding client
  636. # On machine A: python -c 'import rpc; rpc.testsvr()'
  637. # On machine B: python -c 'import rpc; rpc.testclt()' A
  638. # (A may be == B)
  639.  
  640. def testsvr():
  641.     # Simple test class -- proc 1 doubles its string argument as reply
  642.     class S(UDPServer):
  643.         def handle_1(self):
  644.             arg = self.unpacker.unpack_string()
  645.             self.turn_around()
  646.             print 'RPC function 1 called, arg', `arg`
  647.             self.packer.pack_string(arg + arg)
  648.     #
  649.     s = S('', 0x20000000, 1, 0)
  650.     try:
  651.         s.unregister()
  652.     except RuntimeError, msg:
  653.         print 'RuntimeError:', msg, '(ignored)'
  654.     s.register()
  655.     print 'Service started...'
  656.     try:
  657.         s.loop()
  658.     finally:
  659.         s.unregister()
  660.         print 'Service interrupted.'
  661.  
  662.  
  663. def testclt():
  664.     import sys
  665.     if sys.argv[1:]: host = sys.argv[1]
  666.     else: host = ''
  667.     # Client for above server
  668.     class C(UDPClient):
  669.         def call_1(self, arg):
  670.             return self.make_call(1, arg, \
  671.                 self.packer.pack_string, \
  672.                 self.unpacker.unpack_string)
  673.     c = C(host, 0x20000000, 1)
  674.     print 'making call...'
  675.     reply = c.call_1('hello, world, ')
  676.     print 'call returned', `reply`
  677.